﻿//=============================================================================
// Date		: March 11, 2010
//	Author	: Anthony Paul Ortiz
//	License	: CPOL
//=============================================================================

#region Using Statements

using System;
using System.Collections;
using System.Collections.Generic;
using System.Collections.Specialized;
using System.ComponentModel;
using System.Linq;
using System.Threading;
using System.Windows;
using System.Windows.Threading;

#endregion Using Statements

namespace WpfAdvControls.Plate
{
    public class NotifyCollectionChangedWrapper<T> : IList<T>, IList, INotifyCollectionChanged, INotifyPropertyChanged,
        IWeakEventListener
    {
        private readonly Dictionary<NotifyCollectionChangedEventHandler, CollectionChangedWrapperEventData>
            collectionChangedHandlers;

        #region Constructors

        public NotifyCollectionChangedWrapper(INotifyCollectionChanged oc)
        {
            internalOC = oc;
            collectionChangedHandlers =
                new Dictionary<NotifyCollectionChangedEventHandler, CollectionChangedWrapperEventData>();

            AsIListT = internalOC as IList<T>;
            AsIList = internalOC as IList;
            AsICollection = internalOC as ICollection;
            AsICollectionT = internalOC as ICollection<T>;
            AsIEnumerable = internalOC as IEnumerable;
            AsIEnumerableT = internalOC as IEnumerable<T>;

            CollectionChangedEventManager.AddListener(internalOC, this);
        }

        #endregion Constructors

        #region INotifyCollectionChanged Members

        public event NotifyCollectionChangedEventHandler CollectionChanged
        {
            add
            {
                //Dispatcher dispatcher = Dispatcher.CurrentDispatcher; // should always work
                var dispatcher = Dispatcher.FromThread(Thread.CurrentThread);
                // experimental (can return null)...
                collectionChangedHandlers.Add(value,
                    new CollectionChangedWrapperEventData(dispatcher,
                        args => value(this, args)));
            }
            remove { collectionChangedHandlers.Remove(value); }
        }

        #endregion

        #region IWeakEventListener Members

        public bool ReceiveWeakEvent(Type managerType, object sender, EventArgs e)
        {
            internalOC_CollectionChanged(sender, e as NotifyCollectionChangedEventArgs);
            return true;
        }

        #endregion

        #region Handlers

        private void internalOC_CollectionChanged(object sender, NotifyCollectionChangedEventArgs e)
        {
            var handlers =
                collectionChangedHandlers.ToArray();

            if (handlers.Length > 0)
            {
                foreach (var kvp in handlers)
                {
                    if (kvp.Value.Dispatcher == null)
                    {
                        kvp.Value.Action(e);
                    }
                    else
                    {
                        kvp.Value.Dispatcher.Invoke(kvp.Value.Action, DispatcherPriority.DataBind, e);
                    }
                }
            }
        }

        #endregion Handlers

        #region Private Fields

        private readonly ICollection AsICollection;
        private readonly ICollection<T> AsICollectionT;
        private readonly IEnumerable AsIEnumerable;
        private readonly IEnumerable<T> AsIEnumerableT;
        private readonly IList AsIList;
        private readonly IList<T> AsIListT;
        private readonly INotifyCollectionChanged internalOC;

        #endregion Private Fields

        #region IList Members

        public int Add(object value)
        {
            return AsIList.Add(value);
        }

        public bool Contains(object value)
        {
            return AsIList.Contains(value);
        }

        public int IndexOf(object value)
        {
            return AsIList.IndexOf(value);
        }

        public void Insert(int index, object value)
        {
            AsIList.Insert(index, value);
        }

        public bool IsFixedSize
        {
            get { return AsIList.IsFixedSize; }
        }

        public void Remove(object value)
        {
            AsIList.Remove(value);
        }

        object IList.this[int index]
        {
            get { return AsIList[index]; }
            set { AsIList[index] = value; }
        }

        public void CopyTo(Array array, int index)
        {
            AsICollection.CopyTo(array, index);
        }

        public bool IsSynchronized
        {
            get { return AsICollection.IsSynchronized; }
        }

        public object SyncRoot
        {
            get { return AsICollection.SyncRoot; }
        }

        #endregion

        #region IList<T> Members

        public int IndexOf(T item)
        {
            return AsIListT.IndexOf(item);
        }

        public void Insert(int index, T item)
        {
            AsIListT.Insert(index, item);
        }

        public void RemoveAt(int index)
        {
            AsIListT.RemoveAt(index);
        }

        public T this[int index]
        {
            get { return AsIListT[index]; }
            set { AsIListT[index] = value; }
        }

        public void Add(T item)
        {
            AsICollectionT.Add(item);
        }

        public void Clear()
        {
            AsICollectionT.Clear();
        }

        public bool Contains(T item)
        {
            return AsICollectionT.Contains(item);
        }

        public void CopyTo(T[] array, int arrayIndex)
        {
            AsICollectionT.CopyTo(array, arrayIndex);
        }

        public int Count
        {
            get { return AsICollectionT.Count; }
        }

        public bool IsReadOnly
        {
            get { return AsICollectionT.IsReadOnly; }
        }

        public bool Remove(T item)
        {
            return AsICollectionT.Remove(item);
        }

        public IEnumerator<T> GetEnumerator()
        {
            return AsIEnumerableT.GetEnumerator();
        }

        IEnumerator IEnumerable.GetEnumerator()
        {
            return AsIEnumerable.GetEnumerator();
        }

        #endregion

        #region INotifyPropertyChanged Members

        public event PropertyChangedEventHandler PropertyChanged;

        private void NotifyPropertyChanged(string property)
        {
            if (PropertyChanged != null)
            {
                PropertyChanged(this, new PropertyChangedEventArgs(property));
            }
        }

        #endregion
    }
}